package com.nulldev.util.java.swing.font;

import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Font;
import java.awt.GraphicsEnvironment;
import java.util.ArrayList;
import java.util.LinkedList;

import javax.swing.JDesktopPane;
import javax.swing.JEditorPane;
import javax.swing.JInternalFrame;
import javax.swing.JMenu;
import javax.swing.JPanel;

import com.nulldev.util.java.JVMUtil;
import com.nulldev.util.logging.Logger;
import com.nulldev.util.logging.LoggerFactory;
import com.nulldev.util.os.OSUtil;
import com.nulldev.util.other.ArrayUtils;

public class FontManager {
	public static Font defaultFont() {
		return new JPanel().getFont();
	}

	public static Font defaultFont(final float size) {
		return defaultFont().deriveFont((float) size);
	}
	
	private static Font lcd, young, hw_g, traffic, vcrOSD, mime;
	
	public static Font getLCD() {
		if (lcd == null) {
			try {
				lcd = Font.createFont(Font.TRUETYPE_FONT, FontManager.class.getResourceAsStream("fonts/LEDCalculator.ttf"));
				GraphicsEnvironment.getLocalGraphicsEnvironment().registerFont(lcd);
				return lcd;
			}catch (Exception e) {
				e.printStackTrace();
				return defaultFont();
			}
		}
		return lcd;
	}
	
	public static Font getMimeFont() {
		if (mime == null) {
			try {
				mime = Font.createFont(Font.TRUETYPE_FONT, FontManager.class.getResourceAsStream("fonts/MODENINE.TTF"));
				GraphicsEnvironment.getLocalGraphicsEnvironment().registerFont(mime);
				return mime;
			}catch (Exception e) {
				e.printStackTrace();
				return defaultFont();
			}
		}
		return mime;
	}
	
	public static Font getVCR() {
		if (vcrOSD == null) {
			try {
				vcrOSD = Font.createFont(Font.TRUETYPE_FONT, FontManager.class.getResourceAsStream("fonts/VCR_OSD_MONO_1.001.ttf"));
				GraphicsEnvironment.getLocalGraphicsEnvironment().registerFont(vcrOSD);
				return vcrOSD;
			}catch (Exception e) {
				e.printStackTrace();
				return defaultFont();
			}
		}
		return vcrOSD;
	}
	
	public static Font getVCR(final float size) {
		return getVCR().deriveFont((float) size);
	}
	
	public static Font getHighway() {
		if (hw_g == null) {
			try {
				hw_g = Font.createFont(Font.TRUETYPE_FONT, FontManager.class.getResourceAsStream("fonts/HWYGOTH.ttf"));
				GraphicsEnvironment.getLocalGraphicsEnvironment().registerFont(hw_g);
				return hw_g;
			}catch (Exception e) {
				e.printStackTrace();
				return defaultFont();
			}
		}
		return hw_g;
	}
	
	public static Font getHighway(final float size) {
		return getHighway().deriveFont((float) size);
	}

	public static Font getLCD(final float size) {
		return getLCD().deriveFont((float) size);
	}
	
	public static Font getMimeFont(final float size) {
		return getMimeFont().deriveFont((float) size);
	}
	
	public static Font getTraffic() {
		if (traffic == null) {
			try {
				traffic = Font.createFont(Font.TRUETYPE_FONT, FontManager.class.getResourceAsStream("fonts/TRAFFIC2_0.ttf"));
				GraphicsEnvironment.getLocalGraphicsEnvironment().registerFont(traffic);
				return traffic;
			}catch (Exception e) {
				e.printStackTrace();
				return defaultFont();
			}
		}
		return traffic;
	}
	
	public static Font getTraffic(final float size) {
		return getTraffic().deriveFont((float) size);
	}
	
	public static Font getYoung() {
		if (young == null) {
			try {
				young = Font.createFont(Font.TRUETYPE_FONT, FontManager.class.getResourceAsStream("fonts/Young.ttf"));
				GraphicsEnvironment.getLocalGraphicsEnvironment().registerFont(young);
				return young;
			}catch (Exception e) {
				e.printStackTrace();
				return defaultFont();
			}
		}
		return young;
	}

	public static Font getYoung(final float size) {
		return getYoung().deriveFont((float) size);
	}

	private static final LinkedList<SWTItem> modules = new LinkedList<SWTItem>();
	public static int fontSize = 16;
	public static Font setFont = getHighway(fontSize);

	public static void registerAll(final Container c) {
		registerAll(c, false);
	}
	
	public static void registerAll(final Container c, final boolean disableOut) {
		final ArrayList<Component> items = (ArrayList<Component>) getAllComponents(c);
		int r = 0;
		for (final Component cmp : items) {
			if (!(cmp instanceof JDesktopPane && cmp instanceof JInternalFrame)) {
				final SWTItem item = new SWTItem(cmp);
				if (!modules.contains(item)) {
					modules.add(item);
					r += 1;
				}
			}
		}
		if (!modules.isEmpty()) {
			if (!disableOut) {
				logger.info("Found '" + items.size() + "' items, registered '" + r + "'.");
			}
		}else {
			if (!disableOut) {
				logger.info("Found '" + items.size() + "' items, registered '" + (modules.size() - r) + "'.");
			}
		}
	}
	
	private static final Logger logger = LoggerFactory.getLogger(true, true);

	public static ArrayList<Component> getAllComponentsA(final Container c) {
		if (c == null) {
			logger.warn("getAllComponentsA(Container) -> Input was null, returning blank list!");
			return new ArrayList<Component>();
		}
	    Component[] comps = c.getComponents();
	    ArrayList<Component> compList = new ArrayList<Component>();
	    for (Component comp : comps) {
	        if (comp instanceof JDesktopPane || comp instanceof JInternalFrame) {
	    	}else if (comp instanceof Container) {
	    		compList.add(comp);
	            compList.addAll(getAllComponents((Container) comp));
	    	}else if (comp instanceof JMenu) {
	        	compList.add(comp);
	        	final JMenu menu = (JMenu) comp;
	        	compList.addAll(ArrayUtils.toArrayList(menu.getMenuComponents()));
	        }else {
		        compList.add(comp);
	        }
	    }
	    return compList;
	}
	
	public static ArrayList<Component> getAllComponents(final Container c) {
		final ArrayList<Component> out = new ArrayList<Component>();
		final LinkedList<Component> items = new LinkedList<Component>();
		for (final Component a : FontManager.getAllComponentsA(c)) {
			items.add(a);
		}
		int its = 0;
		while (!items.isEmpty()) {
			for (final Component cx : (LinkedList<Component>) items.clone()) {
				if (cx instanceof JDesktopPane || cx instanceof JInternalFrame) {
				}else if (cx instanceof JMenu) {
					items.addAll(ArrayUtils.toArrayList(((JMenu) cx).getMenuComponents()));
				}else {
					out.add(items.removeFirst());
				}
			}
		}
		return out;
	}
	
	public static int getFontSize() {
		return fontSize;
	}
	
	public static Font getFontSet() {
		return setFont.deriveFont((float) fontSize);
	}
	
	public static Font getFontSet(final float fontSize) {
		return setFont.deriveFont((float) fontSize);
	}
	
	public static LinkedList<SWTItem> getModules() {
		return modules;
	}

	public static Font defaultFor(final Component cmp) {
		for (final SWTItem local : modules) {
			if (local.item() == cmp) {
				return local.font();
			}
		}
		return defaultFont();
	}
	
	public static Color defaultForeground(final Component cmp) {
		for (final SWTItem local : modules) {
			if (local.item() == cmp) {
				return local.foreground();
			}
		}
		return Color.LIGHT_GRAY;
	}
	
	public static Color defaultBackground(final Component cmp) {
		for (final SWTItem local : modules) {
			if (local.item() == cmp) {
				return local.background();
			}
		}
		return Color.BLACK;
	}
	
	public static void applyAll(final Font f) {
		if (modules == null) {
			LoggerFactory.getLogger(true, true).error("[applyAll()] modules was null!");
			return;
		}
		setFont = f;
		for (final SWTItem local : modules) {
			if (local != null) {
				local.setFont(f);
			}
		}
	}
	
	private static final Font mono = new JEditorPane().getFont();
	public static final Font getLocalFont(final String text) {
		if (text.equals("Highway Gothic")) {
			return getHighway();
		}else if (text.equals("Traffic")) {
			return getTraffic();
		}else if (text.equals("VCR OSD Mono")) {
			return getVCR();
		}else if (text.equals("Young")) {
			return getYoung();
		}else if (text.equals("LCD")) {
			return getLCD();
		}else if (text.equals("mono")) {
			return mono;
		}
		return defaultFont(12);
	}
	
	public static final String[] REGISTERED_FONTS = {"Highway Gothic", "Traffic", "VCR OSD Mono", "Young", "LCD",
			"mono"};
	public static Font getFont(final String text) {
		//System.out.println("getFont(text) -> " + text);
		try {
			final Font f = Font.decode(text);
			if (f == null || !f.getFamily().equals(text)){
				//System.out.println("f == null!");
				for (final String font : REGISTERED_FONTS) {
					//System.out.println(font);
					if (font.equals(text)) {
						return getLocalFont(text);
					}
				}
				throw new Exception();
			}
			//System.out.println(f);
			return f;
		}catch (Exception e) {
			try {
				for (final String font : REGISTERED_FONTS) {
					//System.out.println(font);
					if (font.equals(text)) {
						return getLocalFont(text);
					}
				}
			}catch (Exception ea) {
				return defaultFont(12);
			}
		}
		return defaultFont(12);
	}
	
	public static Font getFonts(final String... text) {
		for (final String te : text) {
			if (te != null) {
				final Font f = getFont(te);
				if (!f.equals(defaultFont(12))) {
					return f;
				}
			}
		}
		return defaultFont(12);
	}
	
	public static ArrayList<String> getGlobalFonts() {
		return ArrayUtils.toArrayList(ArrayUtils.addAll(
				GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames(), REGISTERED_FONTS));
	}
	
	public static ArrayList<Font> getGlobalFontsBeta() {
		return ArrayUtils.toArrayList
				(GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts());
	}

	public static void setFont(final Font font, final int fontSize) {
		setFont = font;
		FontManager.fontSize = fontSize;
	}

	public static Font randomFont() {
		return FontManager.getFont(ArrayUtils.random(getGlobalFonts()));
	}

	public static void loadSystemSpecificFonts() {
		final Font enhanced = getFonts("SF Pro Display", "MADE Evolve Sans", "Biko", "Neep Regular");
		if (!enhanced.equals(defaultFont(12))) {
			setFont(enhanced, 16);
			return;
		}
		if (OSUtil.isWindows()) {
			setFont(getFonts("Segoe UI Regular", "Segoe UI", "Verdana"), 16);
			return;
		}else if (OSUtil.isLinux()) {
			setFont(getFonts("Noto Sans", "Roboto"), 16);
			return;
		}else if (OSUtil.isMac()) {
			setFont(getFonts("Helvetica", "Lucida Grande", "Chicago"), 16);
			return;
		}
	}
	
	private static final Font SERIF = getFont("Serif");
	
	public static Font getSerif() {
		return SERIF;
	}
	
	public static Font getSerif(final int size) {
		return SERIF.deriveFont((float) size);
	}

	public static void __print_fonts() {
		for (final String fontFace : getGlobalFonts()) {
			JVMUtil.async_println(fontFace);
		}
	}
}
